#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>


#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

static const uint16_t gport = 8081;
#define BACKLOG 8

using namespace LogModule;

class TcpServer
{
public:
	TcpServer(int port = gport)
		: _port(port)
	{
	}
	void InitServer()
	{
		// 1.创建tcp sockfd
		// int socket(int domain, int type, int protocol);

		_listensockfd = ::socket(AF_INET, SOCK_STREAM, 0); // SOCK_STREAM：提供有序、可靠、双向、基于连接的字节流
		if (_listensockfd < 0)
		{
			LOG(LogLevel::FATAL) << "socket error";
			Die(SOCKET_ERR);
		}
		LOG(LogLevel::INFO) << "socket create sucess,sockfd is: " << _listensockfd;

		// 1.1 填充socket信息

		struct sockaddr_in local;
		memset(&local, 0, sizeof(local));
		local.sin_family = AF_INET;
		local.sin_port = ::htons(gport);
		local.sin_addr.s_addr = INADDR_ANY; // 绑定任意ip地址

		// 2.bind
		// int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

		int n = ::bind(_listensockfd, CONV(&local), sizeof(local));
		if (n < 0)
		{
			LOG(LogLevel::FATAL) << "bind error";
			Die(BIND_ERR);
		}
		LOG(LogLevel::INFO) << "bind sucess,sockfd is: " << _listensockfd;

		// 3. tcp是面向连接的，就要求tcp随时随地等待被连接
		// int listen(int sockfd, int backlog);  backlog：指定了等待连接队列的最大长度

		n = ::listen(_listensockfd, BACKLOG);
		if (n < 0)
		{
			LOG(LogLevel::FATAL) << "listen error";
			Die(LISTEN_ERR);
		}
		LOG(LogLevel::INFO) << "listen sucess,sockfd is: " << _listensockfd;
	
		//::signal(SIGCHLD,SIG_IGN); // 子进程退出，OS会自动回收资源，不用在wait()
	}

	void HandlerRequest(int sockfd) // tcp也是全双工，一个文件描述符可以读可以写
	{
		LOG(LogLevel::INFO) << "HandlerRequest,sockfd is: " << sockfd;
		char inbuffer[4096];
		while (1)
		{
			ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer) - 1);
			if (n > 0)
			{
				inbuffer[n] = 0;
				LOG(LogLevel::INFO) << inbuffer;

				std::string echo_str = "server echo# ";
				echo_str += inbuffer;

				::write(sockfd, echo_str.c_str(), echo_str.size());
			}
			else if (n == 0) // read 如果读取返回值是0，表示client退出
			{
				LOG(LogLevel::INFO) << "client quit" << sockfd;
				break;
			}
			else // 读取失败了
				break;
		}
		::close(sockfd); // fd泄漏问题
	}

////////////////////////////////////////////////////////////
// 为多线程做准备
private:
	struct ThreadData
	{
		int sockfd;
		TcpServer* self;
	};
public:
	static void * ThreadEntry(void* args) // static 因为 pthreads 要求线程函数是全局/静态的调用约定
	{
		// 分离线程：让线程成为“分离态”，结束后自动回收资源，无需主线程 pthread_join避免主线程因等待线程而阻塞

		pthread_detach(pthread_self());
		ThreadData* data = (ThreadData*)args;
		data->self->HandlerRequest(data->sockfd); // static静态成员函数，无法访问类内普通成员函数/普通成员
		return nullptr;
	}

///////////////////////////////////////////////////////////////
	void Start()
	{
		_isrunning = true;
		while (_isrunning)
		{
			// 不能直接读取数据

			// 1.获取新连接
			// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

			struct sockaddr_in peer;
			socklen_t peerlen = sizeof(peer);
			LOG(LogLevel::DEBUG) << "accept ing...";

			// 获取client的信息：数据从sockfd获取，client的网络地址socket信息从accept获取||udp是recvfrom
			int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);
			if (sockfd < 0)
			{
				LOG(LogLevel::WARNING) << "accept error" << strerror(errno);
				continue;
			}

			// 2.获取连接成功
			LOG(LogLevel::INFO) << "accept sucess,sockfd is" << sockfd;
			InetAddr addr(peer);
			LOG(LogLevel::INFO) << "client info: " << addr.Addr();

			// verson 0
			// HandlerRequest(sockfd);

			// verson 1 多进程版本

			pid_t id = fork();
			if(id == 0)
			{
				// child  子进程继承父进程的文件描述符表，父子进程各一张
				// 1.关闭不需要的fd
				::close(_listensockfd);

				if(fork() > 0) exit(0); // 子进程退出

				// 孙子进程处理任务 -- 孙子进程是孤儿进程 -- 由系统init回收
				HandlerRequest(sockfd);  // 孙子进程处理需求，处理完后退出孙子进程
				exit(0);
			}

			::close(sockfd);
			int rid = ::waitpid(id,nullptr,0); // 回收子进程
			if(rid < 0)
			{
				LOG(LogLevel::WARNING) << "waitpid error";
			}


			// verson 2 多线程版本
			// 主线程和新线程共享一张文件描述符表！

			pthread_t tid;
			ThreadData * data = new ThreadData; 
			data->sockfd = sockfd;
			data->self = this;
			pthread_create(&tid,nullptr,ThreadEntry,data);
		}
	}
	~TcpServer()
	{
	}

private:
	int _listensockfd; // 监听socket
	uint16_t _port;
	bool _isrunning;
};